package net.lzzy.practices.services;

import net.lzzy.practices.entities.Applicant;
import net.lzzy.practices.entities.Course;
import net.lzzy.practices.entities.User;
import net.lzzy.practices.repositories.ApplicantRepository;
import net.lzzy.practices.repositories.CourseRepository;
import net.lzzy.practices.repositories.UserRepository;
import net.lzzy.practices.utils.AuthUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.UUID;

@Service
public class ApplicantServiceImpl implements ApplicantService{
    private final ApplicantRepository repository;
    private final CourseRepository cRepository;
    private final UserRepository uRepository;

    @Autowired
    public ApplicantServiceImpl(ApplicantRepository repository, CourseRepository cRepository, UserRepository uRepository) {
        this.repository = repository;
        this.cRepository = cRepository;
        this.uRepository = uRepository;
    }

    @Override
    public Applicant apply(UUID studentId, UUID courseId, String token) {
        if (AuthUtils.invalid(studentId,token)){
            return null;
        }
        User student = uRepository.findById(studentId).orElse(null);
        Course course = cRepository.findById(courseId).orElse(null);
        if (student == null || course == null){
            return null;
        }
        Applicant applicant = new Applicant();
        applicant.setCourse(course);
        applicant.setStudent(student);
        applicant.setStatus(Applicant.TYPE_APPLYING);
        return repository.save(applicant);
    }

    @Override
    public List<Applicant> getAllApplicants(UUID studentId) {
        return null;
    }

    @Override
    public List<Applicant> getEnrolledApplicants(UUID studentId) {
        return repository.findApplicantsByStudentIdEqualsAndStatusEqualsOrderByApplyTimeDesc(studentId,Applicant.TYPE_APPROVED);
    }

    @Override
    public List<Applicant> getApplyingApplicants(UUID studentId) {
        return repository.findApplicantsByStudentIdEqualsAndStatusEqualsOrderByApplyTimeDesc(studentId,Applicant.TYPE_APPLYING);
    }

    @Override
    public List<Applicant> getDeclinedApplicants(UUID studentId) {
        return repository.findApplicantsByStudentIdEqualsAndStatusEqualsOrderByApplyTimeDesc(studentId,Applicant.TYPE_DECLINED);
    }

    private Applicant update(UUID applicationId, String token,int status){
        Applicant applicant = repository.findById(applicationId).orElse(null);
        if (applicant == null){
            return null;
        }
        Course course = applicant.getCourse();
        UUID teacherId = course.getTeacher().getId();
        if (AuthUtils.invalid(teacherId,token)){
            return null;
        }
        applicant.setStatus(status);
        return repository.save(applicant);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Applicant approve(UUID applicationId, String token) {
        Applicant applicant = update(applicationId,token,Applicant.TYPE_APPROVED);
        if (applicant != null){
            Course course = applicant.getCourse();
            User student = applicant.getStudent();
            List<User> students = course.getStudents();
            if (!students.contains(student)){
                students.add(student);
            }
            course.setStudents(students);
            applicant.setCourse(cRepository.save(course));
        }
        return applicant;
    }

    @Override
    public Applicant decline(UUID applicationId, String token) {
        return update(applicationId,token,Applicant.TYPE_DECLINED);
    }

    @Override
    public List<Applicant> getEnrolledStudents(UUID courseId) {
        return repository.findApplicantsByCourseIdEqualsAndStatusEqualsOrderByApplyTimeDesc(courseId,Applicant.TYPE_APPROVED);
    }

    @Override
    public List<Applicant> getApplyingStudents(UUID courseId) {
        return repository.findApplicantsByCourseIdEqualsAndStatusEqualsOrderByApplyTimeDesc(courseId,Applicant.TYPE_APPLYING);
    }

    @Override
    public List<Applicant> getDesclingStudents(UUID courseId) {
        return repository.findApplicantsByCourseIdEqualsAndStatusEqualsOrderByApplyTimeDesc(courseId,Applicant.TYPE_DECLINED);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Course removeStudent(UUID courseId, UUID studentId, String token) {
        Course course = cRepository.findById(courseId).orElse(null);
        if (course == null) {
            return null;
        }
        UUID teacherId = course.getTeacher().getId();
        if (AuthUtils.invalid(teacherId,token)){
            return null;
        }
        course.getStudents().removeIf(f -> f.getId().equals(studentId));
        Applicant applicant = repository.findApplicantByCourseIdAndStudentId(courseId,studentId);
        if (applicant != null){
            update(applicant.getId(),token,Applicant.TYPE_DECLINED);
        }
        return cRepository.save(course);
    }
}
